package huanzhao.erp.service;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import huanzhao.erp.domain.entity.*;
import huanzhao.erp.domain.enumerate.error.ValidationError;
import huanzhao.erp.domain.po.Result;
import huanzhao.erp.domain.po.Scope;
import huanzhao.erp.domain.types.DepartmentType;
import huanzhao.erp.domain.types.PermissionTarget;
import huanzhao.erp.domain.types.PermissionType;
import huanzhao.erp.domain.vo.ExternalAccountVO;
import huanzhao.erp.domain.vo.StaffVO;
import huanzhao.erp.domain.vo.TaskVO;
import huanzhao.erp.mapper.*;
import huanzhao.erp.other.StpInterfaceImpl;
import huanzhao.erp.util.AESUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;
import java.lang.Exception;
import java.util.*;


/**
 * @author April
 * 2024/6/12 下午4:04
 * @version 1.0
 */
@Service
@Slf4j
public class StaffService extends ServiceImpl<StaffMapper, Staff> {
    final BasicService basicService;
    final HttpSession session;
    private final StaffMapper staffMapper;
    private final TaskMapper taskMapper;
    private final TeamMapper teamMapper;
    private final PositionMapper positionMapper;
    private final ConvertService convertService;
    private final ExternalAccountMapper externalAccountMapper;
    private final PermissionService permissionService;
    private final ExceptionMapper exceptionMapper;

    public StaffService(BasicService basicService, HttpSession session, StaffMapper staffMapper, TaskMapper taskMapper, TeamMapper teamMapper, PositionMapper positionMapper, ConvertService convertService, ExternalAccountMapper externalAccountMapper, PermissionService permissionService, PermissionService permissionService1, ExceptionMapper exceptionMapper) {
        this.basicService = basicService;
        this.session = session;
        this.staffMapper = staffMapper;
        this.taskMapper = taskMapper;
        this.teamMapper = teamMapper;
        this.positionMapper = positionMapper;
        this.convertService = convertService;
        this.externalAccountMapper = externalAccountMapper;
        this.permissionService = permissionService1;
        this.exceptionMapper = exceptionMapper;
    }

    /**
     * 员工登录
     *
     * @param phone    手机
     * @param password 密码
     * @return Result<?>
     * 2024/6/12 上午10:27:56
     */
    public Result<?> login(String phone, String password) {
        // 员工登录
        Staff staff = getStaffByPhone(phone);
        if (staff == null || !Objects.equals(staff.getPassword(), password)) {
            return Result.error(ValidationError.PASSWORD_ERROR);
        }
        if (staff.getResigned()) {
            return Result.error("离职人员无法登录");
        }
        StpUtil.login(staff.getId());
        // 如果是项目经理，拥有所有权限
        if (staff.getId().equals(basicService.getGlobalSetting().getManagerId())) {
            // 更新session中的员工信息
            staff.setPermission("*");
        }
        // 将员工加入到session中
        StpUtil.getSession().set("staff", staff);
        return Result.ok("登录成功", StpUtil.getTokenInfo().getTokenValue());
    }

    public Staff getStaffBasicById(Long id) {
        return staffMapper.getStaffBasicById(id);
    }

    public Staff getStaffByPhone(String phone) {
        return staffMapper.getStaffByPhone(phone);
    }

    /**
     * 检查员工信息是否正确
     * 只对该不为空的字段进行检查
     *
     * @return Result<?>
     * 2024/10/4 17:50:57
     */
    public Result<?> checkStaffInfo(String name, String phone, String password) {
        if (StringUtils.isNotBlank(name) && name.length() > 30) {
            return Result.error(ValidationError.VALIDATION_ERROR, "员工姓名过长");
        }
        if (StringUtils.isNotBlank(password) && password.length() != 64) {
            return Result.error(ValidationError.PASSWORD_LENGTH_ILLEGAL);
        }
        // !Pattern.matches("^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$", phone)
        if (StringUtils.isNotBlank(phone) && (phone.length() < 11 || phone.length() > 15)) {
            return Result.error(ValidationError.PHONE_NUMBER_FORMAT_ERROR, "员工手机号不合法");
        }
        return Result.ok();
    }

    public int getStaffRunningTasksNum(Long staffId) {
        return taskMapper.getStaffRunningTasksNum(staffId);
    }

    /**
     * 检查员工是否在某一团队中
     *
     * @param staffId 员工id
     * @param teamId  团队id
     * @return boolean
     * 2024/6/12 下午4:32:41
     */
    public boolean checkStaffInTeam(Long staffId, Long teamId) {
        return positionMapper.checkStaffInTeam(staffId, teamId);
    }

    public Result<?> changePass(long staffId, String newPass) {
        try {
            staffMapper.updateStaffPass(staffId, newPass);
            if (staffId == basicService.getCurrentStaffId()) {
                // 更新session中的员工信息
                basicService.getCurrentStaff().setPassword(newPass);
                StpUtil.getSession().update();
            } else {
                // 如果修改的是其他员工的密码，将其踢出登录
                StpUtil.logout(staffId);
            }
            return Result.ok("密码修改成功");
        } catch (Exception e) {
            log.error("密码修改失败", e);
            return Result.error("其他原因出错，请联系开发人员");
        }
    }

    public Result<?> updateStaffName(long staffId, String name) {
        try {
            staffMapper.updateStaffName(staffId, name);
            if (staffId == basicService.getCurrentStaffId()) {
                // 更新session中的员工信息
                basicService.getCurrentStaff().setName(name);
                StpUtil.getSession().update();
            }
            return Result.ok("姓名修改成功");
        } catch (Exception e) {
            log.error("姓名修改失败", e);
            return Result.error("其他原因出错，请联系开发人员");
        }
    }

    public Result<?> updateStaffPhone(long staffId, String phone) {
        try {
            Staff staff = basicService.getCurrentStaff();
            staffMapper.updateStaffPhone(staffId, phone);
            if (staffId == basicService.getCurrentStaffId()) {
                // 更新session中的员工信息
                staff.setPhone(phone);
                StpUtil.getSession().update();
            }
            return Result.ok("手机号修改成功");
        } catch (Exception e) {
            log.error("手机号修改失败", e);
            return Result.error("其他原因出错，请联系开发人员");
        }
    }

    /**
     * 按条件查询员工的任务
     *
     * @param teamId   团队id
     * @param state    任务状态
     * @param type     任务类型
     * @param pageNum  当前页码
     * @param pageSize 每页记录数量
     * @return IPage<Task>
     * 2024/6/13 下午5:17:47
     */
    public IPage<TaskVO> getStaffTasksByCondition(Long teamId, String type, String state, Integer pageNum, Integer pageSize) {
        IPage<Task> page = taskMapper.getStaffTasksByCondition(basicService.getCurrentStaffId(), teamId, type, state, Page.of(pageNum, pageSize));
        if (page == null) {
            return new Page<>();
        }
        List<TaskVO> tasks = new ArrayList<>();
        page.getRecords().forEach(task -> tasks.add(convertService.convertTask(task)));
        return new Page<TaskVO>().setRecords(tasks).setTotal(page.getTotal());
    }


    /**
     * 检查员工是否为财务人员
     *
     * @param staffId 员工id
     * @return boolean
     * 2024/6/18 下午4:29:18
     */
    public boolean checkStaffIsFinancer(Long staffId) {
        return false;
    }

    /**
     * 按类型查询团队里面的员工
     *
     * @param teamId   团队id
     * @param taskType 任务类型
     * @param up       是否为上架员工
     * @param qc       是否为质检员工
     * @param input    是否为录入员工
     * @return List<Staff>
     * 2024/6/18 下午5:58:48
     */
    public List<StaffVO> getStaffInTeamByType(Long teamId, String taskType, boolean up, boolean qc, boolean input) {
        List<Staff> staffList = staffMapper.getStaffInTeamByType(teamId, up, qc, input);
        return fillStaffRunningTasksNum(taskType, staffList);
    }

    /**
     * 根据员工类型获取员工
     *
     * @param taskType   任务类型
     * @param maintainer 是否为维修
     * @param warehouser 是否为仓管
     * @param purchaser  是否为采购
     * @return List<Staff>
     * 2024/6/18 下午7:43:43
     */
    public List<StaffVO> getStaffByType(String taskType, boolean maintainer, boolean warehouser, boolean purchaser) {
        List<Staff> staffList = staffMapper.getStaffByType(maintainer, warehouser, purchaser);
        return fillStaffRunningTasksNum(taskType, staffList);
    }

    /**
     * 根据任务类型填充员工的任务数量
     *
     * @param taskType  任务类型
     * @param staffList 员工列表
     * @return List<Staff>
     * 2024/7/24 上午11:41:40
     */
    private List<StaffVO> fillStaffRunningTasksNum(String taskType, List<Staff> staffList) {
        if (staffList == null) {
            return Collections.emptyList();
        }
        List<StaffVO> result = new ArrayList<>();
        for (Staff staff : staffList) {
            StaffVO staffVO = BeanUtil.copyProperties(staff, StaffVO.class);
            staffVO.setTotalTasksNum(taskMapper.getStaffRunningTasksNum(staff.getId()));
            staffVO.setCurrentTasksNum(taskMapper.getStaffRunningTasksNumByType(staff.getId(), taskType));
            result.add(staffVO);
        }
        return result;
    }


    /**
     * 按条件获取员工信息
     *
     * @param teamId         员工所在团队id
     * @param name           员工名（模糊查询）
     * @param departmentCode 员工所在部门
     * @param pageNum        当前页码
     * @param pageSize       每页记录数量
     * @return IPage<Staff>
     * 2024/6/12 下午3:08:14
     */
    public IPage<StaffVO> getStaffByCondition(Long teamId, String name, Integer departmentCode, Integer pageNum, Integer pageSize) {
        Scope scope = permissionService.getConditionalAuthorizedScope(departmentCode, teamId, PermissionTarget.STAFF, PermissionType.VIEW);
        if (scope.unable()) {
            return new Page<>();
        }
        IPage<Staff> page = staffMapper.getStaffByCondition(name, scope.teamIds(), scope.purchase(), scope.warehouse(), scope.repair(), Page.of(pageNum, pageSize));
        List<StaffVO> staffs = new ArrayList<>();
        page.getRecords().forEach(staff -> staffs.add(convertService.convertStaff(staff)));
        return new Page<StaffVO>().setRecords(staffs).setTotal(page.getTotal());
    }

    /**
     * 添加员工基本信息，不包括部门和权限
     *
     * @param staff 员工信息
     * @return Result<?>
     * 2024/10/3 10:22:42
     */
    public Result<?> addStaff(Staff staff) {
        try {
            staffMapper.addStaff(staff);
            return Result.ok("添加员工成功");
        } catch (Exception e) {
            log.error("添加员工失败", e);
            return Result.error("其他原因出错，请联系开发人员");
        }
    }

    /**
     * 获取所有员工的id和姓名
     *
     * @return List<StaffVO>
     * 2024/10/3 17:12:38
     */
    public List<StaffVO> getAllStaffIdAndName() {
        List<StaffVO> staffList = staffMapper.getAllStaffIdAndName();
        if (staffList == null) {
            return Collections.emptyList();
        }
        return staffList;
    }

    /**
     * 改变员工的休假、离职状态
     *
     * @param type 0->休假状态，1->离职状态
     * @return Result<?>
     * 2024/10/4 14:03:48
     */
    public Result<?> changeStaffState(long staffId, int type) {
        switch (type) {
            case 0 -> staffMapper.changeStaffVacatingState(staffId);
            case 1 -> {
                Staff staff = getById(staffId);
                if (staff == null) {
                    return Result.error("员工不存在，无法离职！");
                }
                if (!staff.getResigned()) {
                    // 校验是否可以离职
                    Result<?> result = checkStaffResignable(staffId);
                    if (result.isError()) {
                        return result;
                    }
                }
                staffMapper.changeStaffResignedState(staffId);
                // 如果是离职，将其踢出登录
                StpUtil.kickout(staffId);
            }
        }
        return Result.ok();
    }

    /**
     * 校验员工是否可以离职
     *
     * @return Result<?>
     * 2024/10/10 11:16:40
     */
    private Result<?> checkStaffResignable(long staffId) {
        GlobalSetting globalSetting = basicService.getGlobalSetting();
        // 先校验员工是否担任关键职务
        String positionName = null;
        if (staffId == globalSetting.getManagerId()) {
            positionName = "项目经理";
        } else if (staffId == globalSetting.getWarehouseHandlerId()) {
            positionName = "仓库异常处理人";
        } else if (staffId == globalSetting.getRepairLeaderId()) {
            positionName = "维修组长";
        } else if (staffId == globalSetting.getPurchaseHandlerId()) {
            positionName = "采购异常处理人";
        } else if (staffId == globalSetting.getFinancerId()) {
            positionName = "财务";
        }
        if (positionName != null) {
            return Result.error("员工正在担任" + positionName + "，无法离职！");
        }


        // 校验员工是否为渠道负责人
        List<Long> teamIds = teamMapper.getAllTeamIds();
        if (teamIds != null) {
            for (Long teamId : teamIds) {
                if (teamMapper.isTeamLeader(staffId, teamId)) {
                    Team team = teamMapper.getTeamById(teamId);
                    return Result.error("员工正在担任" + team.getName() + "渠道负责人，无法离职！");
                }
            }
        }

        // 校验员工手上是否还有进行中的任务
        int taskNum = getStaffRunningTasksNum(staffId);
        if (taskNum > 0) {
            return Result.error("员工有" + taskNum + "个任务尚未完成，无法离职！");
        }

        // 检查员工手上是否有未处理的工单
        int exceptionsNum = exceptionMapper.getStaffHandlingExceptionsNum(staffId);
        if (exceptionsNum > 0) {
            return Result.error("员工有" + exceptionsNum + "个异常事务尚未处理，无法离职！");
        }
        return Result.ok();
    }

    /**
     * 检查员工是否存在
     *
     * @return boolean
     * 2024/10/4 14:21:56
     */
    public boolean notExist(long staffId) {
        return !staffMapper.exist(staffId);
    }

    /**
     * 将员工添加到指定部门中/或者从部门中移除
     *
     * @param staffId        员工id
     * @param departmentType 部门类型，目前只能添加到采购，维修，仓库
     * @param working        是否在职
     * @return Result<?>
     * 2024/10/4 20:29:45
     */
    public Result<?> updateStaffDept(long staffId, DepartmentType departmentType, boolean working) {
        switch (departmentType) {
            case PURCHASE -> staffMapper.updateStaffPurchaser(staffId, working);
            case REPAIR -> staffMapper.updateStaffMaintainer(staffId, working);
            case WAREHOUSE -> staffMapper.updateStaffWarehouser(staffId, working);
        }
        return Result.ok("添加成功");
    }

    /**
     * 获取员工的外部账号
     *
     * @return List<ExternalAccountVO>
     * 2024/10/4 21:31:50
     */
    public List<ExternalAccountVO> getStaffExternalAccount(long staffId) {
        List<ExternalAccount> accountList = externalAccountMapper.getStaffExternalAccount(staffId);
        if (accountList == null) {
            return Collections.emptyList();
        }
        List<ExternalAccountVO> result = new ArrayList<>();
        accountList.forEach(account -> result.add(convertService.convertExternalAccount(account)));
        return result;
    }

    /**
     * 检查外部账号是否正确
     *
     * @return Result<?>
     * 2024/10/4 21:39:25
     */
    public Result<?> checkExternalAccount(ExternalAccount externalAccount, boolean isUpdate) {
        if (externalAccount == null) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号信息不能为空");
        }
        if (StringUtils.isBlank(externalAccount.getName()) || externalAccount.getName().length() > 50) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号名称不合法");
        }
        if (StringUtils.isBlank(externalAccount.getPlatform()) || externalAccount.getPlatform().length() > 50) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号平台不合法");
        }
        if (StringUtils.isBlank(externalAccount.getAccount()) || externalAccount.getAccount().length() > 50) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号不合法");
        }
        if (StringUtils.isBlank(externalAccount.getPassword())) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号密码为空");
        }
        Long teamId = externalAccount.getTeamId();
        Long staffId = externalAccount.getStaffId();
        if (teamId == null || teamId < 0) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号所属团队id不合法");
        }
        if (staffId == null || staffId < 0) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号所属员工id不合法");
        }
        if (isUpdate) {
            if (externalAccount.getId() == null || externalAccount.getId() < 0) {
                return Result.error(ValidationError.VALIDATION_ERROR, "外部账号id不合法");
            }
            ExternalAccount account = externalAccountMapper.selectById(externalAccount.getId());
            if (account == null) {
                return Result.error(ValidationError.VALIDATION_ERROR, "外部账号不存在");
            }
            if (!staffId.equals(account.getStaffId())) {
                return Result.error(ValidationError.VALIDATION_ERROR, "外部账号所属员工不匹配");
            }
        }
        if (!teamMapper.exists(teamId)) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号所属团队不存在");
        }
        if (!staffMapper.exist(staffId)) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号所属员工不存在");
        }
        return Result.ok();
    }

    /**
     * 添加外部账号
     *
     * @return Result<?>
     * 2024/10/4 21:49:06
     */
    public Result<?> addExternalAccount(@NotNull ExternalAccount externalAccount) {
        // 密码经过aes加密
        try {
            externalAccount.setPassword(AESUtil.encrypt(externalAccount.getPassword()));
        } catch (Exception e) {
            log.error("密码加密失败", e);
            return Result.error("密码加密失败");
        }
        externalAccountMapper.insert(externalAccount);
        return Result.ok("添加外部账号成功");
    }

    /**
     * 修改外部账号
     *
     * @return Result<?>
     * 2024/10/4 22:11:19
     */
    public Result<?> updateExternalAccount(ExternalAccount externalAccount) {
        try {
            externalAccount.setPassword(AESUtil.encrypt(externalAccount.getPassword()));
        } catch (Exception e) {
            log.error("密码加密失败", e);
            return Result.error("密码加密失败");
        }
        try {
            externalAccountMapper.updateById(externalAccount);
            return Result.ok("修改外部账号成功");
        } catch (Exception e) {
            log.error("修改外部账号失败", e);
            return Result.error("其他原因出错，请联系开发人员");
        }
    }

    /**
     * 删除外部账号
     *
     * @return Result<?>
     * 2024/10/4 22:38:36
     */
    public Result<?> removeExternalAccount(long eAccountId) {
        ExternalAccount account = externalAccountMapper.selectById(eAccountId);
        if (account == null) {
            return Result.error(ValidationError.VALIDATION_ERROR, "外部账号不存在");
        }
        if (!basicService.getCurrentStaffId().equals(account.getStaffId())) {
            // 修改的不是自己的外部账号，需要鉴权
            StpUtil.checkPermission("company:staff:eaccount:remove");
        }
        externalAccountMapper.deleteById(eAccountId);
        return Result.ok("删除外部账号成功");
    }

    /**
     * 修改员工的权限
     *
     * @return Result<?>
     * 2024/10/4 22:46:07
     */
    public Result<?> updatePermission(long staffId, @NotNull List<String> permissions) {
        if (notExist(staffId)) {
            return Result.error(ValidationError.VALIDATION_ERROR, "员工不存在");
        }
        staffMapper.updatePermissionById(staffId, String.join(",", permissions));
        // 使权限缓存失效
        StpInterfaceImpl.PERMISSION_CACHE.invalidate(staffId);
        return Result.ok("修改权限成功");
    }

    /**
     * 查询员工外部账号的密码
     *
     * @return Result<String>
     * 2024/10/5 09:20:47
     */
    public Result<String> getExternalAccountPassword(long eAccountId) {
        ExternalAccount account = externalAccountMapper.selectById(eAccountId);
        if (account == null) {
            return Result.error("外部账号不存在");
        }
        if (!basicService.getCurrentStaffId().equals(account.getStaffId())) {
            // 不是自己的外部账号，需要鉴权
            StpUtil.checkPermission("company:staff:eaccount:view");
        }
        try {
            return Result.ok(AESUtil.decrypt(account.getPassword()));
        } catch (Exception e) {
            log.error("密码解密失败", e);
            return Result.error("密码解密失败");
        }
    }
}
